namespace effect {

	let $blinkings: Map<number, egret.Tween> = new Map();//闪烁		
	let $rotatings: Map<number, egret.Tween> = new Map();//旋转
	let $zoomings: Map<number, egret.Tween> = new Map();//缩放
	let $shakings: Map<number, egret.Tween> = new Map();//摇摆
	let $floatings: Map<number, egret.Tween> = new Map();//浮动
	let $showings: Map<number, egret.Tween> = new Map();//展示隐藏
	let $options: hjc.AnimationOptions = {};
	let $animate: Function;
	let $tween: egret.Tween;

	function _resetDefault(): void {
		$options.duration = 1; $options.start = 0; $options.end = 1; $options.loop = false;
		$options.wait = 0; $options.times = 1; $options.clockwise = true; $options.oneWay = false;
		$options.override = false; $options.direction = eui.Direction.LTR;
	}


	function _validOptions(obj: egret.DisplayObject, options?: hjc.AnimationOptions, map?: Map<number, egret.Tween>): void {
		if (!obj) { hjc.warn(`animation object is empty!`); return; }
		_resetDefault();
		if (!!options) {
			if (Math.isPositive(options.duration)) $options.duration = options.duration;
			if (Math.notNegtive(options.start)) $options.start = options.start;
			if (Math.notNegtive(options.end)) $options.end = options.end;
			if (!!options.loop) $options.loop = true;
			if (Math.isPositive(options.wait)) $options.wait = options.wait;
			if (Math.isPosInt(options.times)) $options.times = options.times;
			if (options.clockwise === false) $options.clockwise = false;
			if (!options.oneWay) $options.oneWay = true;
			if (!!options.override) $options.override = true;
			if (!!options.direction) $options.direction = options.direction;
			if (isFunc(options.ease)) $options.ease = options.ease;
			if (isFunc(options.call)) $options.call = options.call;
		}
		if (!map) return;
		$tween = null;
		if (map.has(obj.hashCode)) {
			if (!options || !$options.override) map.get(obj.hashCode).setPaused(false);
			else {
				egret.Tween['_tweens'].remove(map.get(obj.hashCode));
				$tween = egret.Tween.get(obj, { loop: $options.loop });
			}
		} else $tween = egret.Tween.get(obj, { loop: $options.loop });
		if (!!$tween) {
			if ($options.wait > 0) $tween.wait($options.wait);
			map.set(obj.hashCode, $tween);
		}
	}

	function _play() {
		if ($options.loop) $animate();
		else {
			for (let time = 0; time < $options.times; time++) $animate();
			if (isFunc($options.call)) $tween.call($options.call);
		}
	}

	export function playBlink<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		_validOptions(obj, options, $blinkings);
		if (!$tween) return;
		let duration = $options.duration * ($options.oneWay ? 1000 : 500);
		obj.alpha = $options.start;
		$animate = () => {
			$tween.to({ alpha: $options.end }, duration, $options.ease);
			if (!$options.oneWay) $tween.to({ alpha: $options.start }, duration, $options.ease);
		};
		_play();
		return obj;
	}

	export function playZoom<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		_validOptions(obj, options, $zoomings);
		if (!$tween) return;
		let duration = $options.duration * ($options.oneWay ? 1000 : 500);
		ui.setScale(obj, $options.start);
		$animate = () => {
			$tween.to({ scaleX: $options.end, scaleY: $options.end }, duration, $options.ease);
			if (!$options.oneWay) $tween.to({ scaleX: $options.start, scaleY: $options.start }, duration, $options.ease);
		}
		_play();
		return obj;
	}

	export function playRotate<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		_validOptions(obj, options, $blinkings);
		if (!$tween) return;
		let _rotation = obj.rotation;
		$animate = () => {
			$tween.to({ rotation: $options.clockwise ? _rotation + 360 : _rotation - 360 },
				$options.duration * 1000, $options.ease);
		}
		_play();
		return obj;
	}

	export function playShake<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		hjc.todo('animation play shake');
		return obj;
	}

	export function playFloat<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		hjc.todo('animation play float');
		return obj;
	}

	export function stopBlink<T extends egret.DisplayObject>(obj: T, reset?: boolean): T {
		if ($blinkings.has(obj.hashCode)) $blinkings.get(obj.hashCode).setPaused(true);
		if (!!reset) obj.alpha = 1;
		return obj;
	}

	export function stopRotate<T extends egret.DisplayObject>(obj: T, reset?: boolean): T {
		if ($rotatings.has(obj.hashCode)) $rotatings.get(obj.hashCode).setPaused(true);
		if (!!reset) obj.rotation = 0;
		return obj;
	}

	export function stopZoom<T extends egret.DisplayObject>(obj: T, reset?: boolean): T {
		if ($zoomings.has(obj.hashCode)) $zoomings.get(obj.hashCode).setPaused(true);
		if (!!reset) ui.setScale(obj, 1);
		return obj;
	}

	export function stopShake<T extends egret.DisplayObject>(obj: T, reset?: boolean): T {
		hjc.todo('animation stop shake');
		return obj;
	}

	export function stopFloat<T extends egret.DisplayObject>(obj: T, reset?: boolean): T {
		hjc.todo('animation stop float');
		return obj;
	}

	export function stopAll<T extends egret.DisplayObject>(obj: T, reset?: boolean, clear?: boolean): T {
		stopRotate(stopRotate(stopBlink(stopRotate(stopBlink(obj, reset), reset), reset), reset), reset);
		if (!!clear) return clearAll(obj);
		return obj;
	}

	export function clearAll<T extends egret.DisplayObject>(obj: T): T {
		$blinkings.delete(obj.hashCode);
		$rotatings.delete(obj.hashCode);
		$zoomings.delete(obj.hashCode);
		$shakings.delete(obj.hashCode);
		$floatings.delete(obj.hashCode);
		return obj;
	}

	export function playClick<T extends egret.DisplayObject>(obj: T, mode: hjc.ClickMode
		= hjc.ClickMode.NONE, complete?: Function, context?: any): T {
		if (!obj) { hjc.warn(`animation object is empty!`); return obj; }
		switch (mode) {
			case hjc.ClickMode.NONE: utils.call(complete, context); break;
			case hjc.ClickMode.SCALE_RCV: _changeScale(obj, () => utils.call(complete, context)); break;
			case hjc.ClickMode.LIGHT_RCV: {
				effect.toLight(obj);
				window.setTimeout(() => { effect.removeColor(obj); utils.call(complete, context); }, game.aniDuration * 1000);
			} break;
			case hjc.ClickMode.BOTH_RCV: {
				effect.toLight(obj);
				_changeScale(obj, () => { effect.removeColor(obj); utils.call(complete, context); });
			} break;
			case hjc.ClickMode.ALPHA_RCV: {
				obj.alpha = game.clickedAlpha;
				window.setTimeout(() => { obj.alpha = 1; utils.call(complete, context); }, game.aniDuration * 1000);
			} break;
			default: hjc.warn(`invalid ClickMode: ${mode}`);
		}
	}

	function _changeScale<T extends egret.DisplayObject>(obj: T, next: Function): void {
		egret.Tween.get(obj).to({ scaleX: game.clickedScale, scaleY: game.clickedScale }, game.aniDuration * 500, egret.Ease.sineOut).
			to({ scaleX: 1, scaleY: 1 }, game.aniDuration * 500, egret.Ease.sineOut).call(() => next());
	}

	export function loopGroup(group: egret.tween.TweenGroup, autoRun: boolean = true): egret.tween.TweenGroup {
		for (let key in group.items) { group.items[key].props = { loop: true }; }
		if (autoRun) group.play(-1);
		return group;
	}

	export function playShow<T extends egret.DisplayObject>(obj: T, mode: hjc.ShowMode = hjc.ShowMode.NONE, parent?: egret.DisplayObjectContainer): T {
		if (!obj) { hjc.warn(`animation object is empty!`); return obj; }
		egret.Tween['_tweens'].remove($showings.get(obj.hashCode));
		if (!!parent) parent.addChild(obj);
		let tween = egret.Tween.get(obj);
		switch (mode) {
			case hjc.ShowMode.NONE: obj.alpha = 1; ui.setScale(obj, 1); break;
			case hjc.ShowMode.SLIGHT_IN:
				obj.alpha = 0; ui.setScale(obj, 0.5);
				tween.to({ alpha: 1, scaleX: 1, scaleY: 1 }, 300, egret.Ease.backOut);
				break;
			case hjc.ShowMode.STRONG_IN:
				obj.alpha = 0; ui.setScale(obj, 0.2);
				tween.to({ alpha: 1, scaleX: 1, scaleY: 1 }, 600, egret.Ease.elasticOut);
				break;
			case hjc.ShowMode.FADE_IN:
				obj.alpha = 0;
				tween.to({ alpha: 1 }, 1000, egret.Ease.sineOut);
				break;
			case hjc.ShowMode.ROTATE_IN: hjc.todo('animation play show rotate in'); break;
			case hjc.ShowMode.FALL_IN: hjc.todo('animation play show fall in'); break;
			case hjc.ShowMode.LTR_IN: hjc.todo('animation play show ltr in'); break;
			case hjc.ShowMode.RTL_IN: hjc.todo('animation play show rtl in'); break;
			case hjc.ShowMode.TTB_IN: hjc.todo('animation play show ttb in'); break;
			case hjc.ShowMode.BTT_IN: hjc.todo('animation play show btt in'); break;
			default: hjc.warn(`invalid ShowMode: ${mode}`);
		}
		if (mode != hjc.ShowMode.NONE) $showings.set(obj.hashCode, tween);
	}

	export function playHide<T extends egret.DisplayObject>(obj: T, mode: hjc.HideMode = hjc.HideMode.NONE,
		remove: boolean = true): T {
		if (!obj) { hjc.warn(`animation object is empty!`); return obj; }
		egret.Tween['_tweens'].remove($showings.get(obj.hashCode));
		let tween = egret.Tween.get(obj);
		switch (mode) {
			case hjc.HideMode.NONE: if (!!remove) ui.remove(obj);
			case hjc.HideMode.SLIGHT_OUT:
				obj.alpha = 1; ui.setScale(obj, 1);
				tween.to({ alpha: 0, scaleX: 0.5, scaleY: 0.5 }, 300, egret.Ease.backOut);
				break;
			case hjc.HideMode.STRONG_OUT:
				obj.alpha = 1; ui.setScale(obj, 1);
				egret.Tween.get(obj).to({ alpha: 0, scaleX: 0.2, scaleY: 0.2 }, 600, egret.Ease.elasticOut);
				break;
			case hjc.HideMode.FADE_OUT:
				obj.alpha = 1;
				tween.to({ alpha: 0 }, 1000, egret.Ease.sineOut);
				break;
			case hjc.HideMode.ROTATE_OUT: hjc.todo('animation play show rotate out'); break;
			case hjc.HideMode.THROW_OUT: hjc.todo('animation play show throw out'); break;
			case hjc.HideMode.LTR_OUT: hjc.todo('animation play show ltr out'); break;
			case hjc.HideMode.RTL_OUT: hjc.todo('animation play show rtl out'); break;
			case hjc.HideMode.TTB_OUT: hjc.todo('animation play show ttb out'); break;
			case hjc.HideMode.BTT_OUT: hjc.todo('animation play show btt out'); break;
			default: hjc.warn(`invalid ShowMode: ${mode}`);
		}
		if (mode != hjc.HideMode.NONE) {
			if (!!remove) tween.call(() => ui.remove(obj));
			$showings.set(obj.hashCode, tween);
		}
	}


	export function playStepIn<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		if (!obj) { hjc.warn(`animation object is empty!`); return obj; }
		_validOptions(obj, options);
		let [i, times] = [0, Math.ceil($options.duration * game.frameRate)];
		let per = ($options.end - $options.start) / times;
		options = { start: $options.start, end: $options.start, duration: $options.duration, direction: $options.direction };
		let ticker = () => {
			toShowPart(obj, options, false);
			if (i < times) {
				options.end += per; i++;
				egret.callLater(() => ticker(), this);
			}
		}
		ticker();
		return obj;
	}

	export function playStepOut<T extends egret.DisplayObject>(obj: T, options?: hjc.AnimationOptions): T {
		if (!obj) { hjc.warn(`animation object is empty!`); return obj; }
		_validOptions(obj, options);
		let [i, times] = [0, Math.ceil($options.duration * game.frameRate)];
		let per = ($options.end - $options.start) / times;
		options = { start: $options.start, end: $options.end, duration: $options.duration, direction: $options.direction };
		let ticker = () => {
			toShowPart(obj, options, false);
			if (i < times) {
				options.start += per; i++;
				egret.callLater(() => ticker(), this);
			}
		}
		ticker();
		return obj;
	}
}